using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using ViewOnCore.Helper.WPF;
using ViewOnCore.HostContract;
using ViewOnCore.Properties;

#region Copyright (c) 2006, 2007, 2008 ViewOn Media Services. All rights reserved.

/*
 Copyright (c) 2006, 2007, 2008 ViewOn Media Services. All rights reserved.
 This work is copyrighted in its entirety. Permission to use "ViewOn.tv"
 material may be granted only after we receive a written request from you,
 and you receive written permission from us.

 While ViewOn Media Services provides the information herein to anyone,
 we retain copyright on all code, text, graphic images, and other content.
 This means that you may not distribute the code, the text or graphics to others without the
 express written permission of ViewOn Media Services; "mirror" this information on your server
 without our permission; or modify or reuse the code, text or graphics on our servers.
 You may print copies of the information for your own personal use; and, you may reference
 the Service from your own documents. Any commercial use of this material is prohibited without
 the written permission of ViewOn Media Services. In all copies of this information, you must
 retain this notice and any other copyright notices originally included with such information.

 This material may be protected by U.S. and International Copyright laws, and you are legally
 bound by the copyright notices on those systems.

 "ViewOn" logos and other official symbols or graphics may not be used in connection with any
 product or service that has not been expressly authorized in advance in writing or in any manner
 that is likely to cause confusion among the users of this or other Web sites or among the public.
 All other seals, graphics or trademarks of organizations not affiliated with ViewOn Media Services
 that appear on this site are the property of their respective owners.

 ViewOn Media Services reserve all other rights. 
*/

#endregion

#region ViewOn.tv Source Code Licence

/*
This license governs use of the ViewOn.tv software. If you use the software, you accept this license. If you do not accept the license, do not use the software.

1. Summary

The source code is made available to view for reference purposes or to contribute to the project only. Developers may not distribute or modify the code for commercial or non-commercial purposes without express Agreement of View On Media Services.

2. Definitions

The terms "reproduce," "reproduction," and "distribution" have the same meaning here as under U.S. copyright law.
"Licensor" means the company "ViewOn Media Services".
"You" means the licensee of the software, who is not engaged in designing, developing, or testing other software, that has the same or substantially the same features or functionality as the software.
"Your company" means the company you worked for when you downloaded the software.
"Reference use" means use of the software within your company as a reference, in read only form, for the sole purposes of debugging and maintaining your products. For clarity, "reference use" does NOT include (a) the right to use the software for purposes of designing, developing, or testing other software, that has the same or substantially the same features or functionality as the software, and (b) the right to distribute the software outside of your company.
"Licensed patents" means any Licensor patent claims which read directly on the software as distributed by the Licensor under this license.

3. Grant of Rights

(A) Copyright Grant- Subject to the terms of this license, the Licensor grants you a non-transferable, non-exclusive, worldwide, royalty-free copyright license to reproduce the software for reference use.
(B) Patent Grant- Subject to the terms of this license, the Licensor grants you a non-transferable, non-exclusive, worldwide, royalty-free patent license under licensed patents for reference use.
4. Limitations
(A) No Trademark License- This license does not grant you any rights to use the Licensor's name, logo, or trademarks.
(B) If you begin patent litigation against the Licensor over patents that you think may apply to the software (including a cross-claim or counterclaim in a lawsuit), your license to the software ends automatically.
(C) The software is licensed "as-is." You bear the risk of using it. The Licensor gives no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the Licensor excludes the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
 */

#endregion

namespace ViewOnCore.Helper.WPF
{

    /// <summary>
    /// This dialog allows to browse a folder and to choose a folder.
    /// </summary>
    public partial class FolderBrowserDialog : System.Windows.Controls.UserControl
    {

        #region Variables

        private bool _isOk;
        private string _selectedPath = null;
        private bool _isDialogClosed = false;

        private Style _treeViewItemStyle;

        #endregion

        #region Constructor

        public FolderBrowserDialog()
        {
            InitializeComponent();

            spinner.Start();

            btnOk.IsEnabled = false;
            btnCancel.IsEnabled = false;
            btnMakeFolder.Visibility = Visibility.Collapsed;
            btnDeleteFolder.Visibility = Visibility.Collapsed;

            btnMakeFolder.Click += new RoutedEventHandler(btnMakeFolder_Click);
            btnDeleteFolder.Click += new RoutedEventHandler(btnDeleteFolder_Click);

            childWindowDecorator.VisuallyReady += new EventHandler(childWindowDecorator_VisuallyReady);
            treeView.SelectedItemChanged += new RoutedPropertyChangedEventHandler<object>(treeView_SelectedItemChanged);

            // HACK
            // ----
            // It is a hack because setting it is XAML does nothing !!!
            Uri uri = new Uri("/ViewOnCore;component/Themes/Generic/FolderBrowserDialog.TreeViewItem.xaml", UriKind.Relative);
            ResourceDictionary dictionary = Application.LoadComponent(uri) as ResourceDictionary;
            _treeViewItemStyle = (Style)dictionary["FolderBrowserDialog.TreeViewItem"];
            treeView.ItemContainerStyle = _treeViewItemStyle;
        }

        void childWindowDecorator_VisuallyReady(object sender, EventArgs e)
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(AsyncAddDrives));
        }

        private void AsyncAddDrives(object state)
        {
            List<DriveInfo> drives = new List<DriveInfo>();

            foreach (DriveInfo dInfo in DriveInfo.GetDrives())
            {
                if (_isDialogClosed)
                    return;

                if (dInfo.IsReady)
                    Dispatcher.Invoke(
                        System.Windows.Threading.DispatcherPriority.Normal,
                        (DelegateInvoker)delegate
                        {
                            treeView.Items.Add(CreateTreeViewItem(dInfo, true));
                        });
            }

            Dispatcher.Invoke(
                        System.Windows.Threading.DispatcherPriority.Normal,
                        (DelegateInvoker)delegate
            {
                spinner.Stop();
                btnOk.IsEnabled = true;
                btnCancel.IsEnabled = true;
            });
        }

        #endregion

        #region Ok / Cancel

        private void btnOk_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            _isDialogClosed = true;

            if (treeView.SelectedItem != null)
            {
                TreeViewItem item = (TreeViewItem)treeView.SelectedItem;
                FBDValue value = item.Tag as FBDValue;
                if (value.Type == FBDValueType.Drive)
                    _selectedPath = value.DriveInfo.Name;
                else
                    _selectedPath = value.DirectoryInfo.FullName;
            }

            _isOk = true;
            childWindowDecorator.Close();
        }

        private void btnCancel_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            _isDialogClosed = true;

            _isOk = false;
            childWindowDecorator.Close();
        }

        #endregion

        #region CreateTreeViewItem

        private TreeViewItem CreateTreeViewItem(DriveInfo dInfo, bool hasChildDirectories)
        {
            return CreateTreeViewItem(new FBDValue(this, dInfo), hasChildDirectories);
        }

        private TreeViewItem CreateTreeViewItem(DirectoryInfo dInfo, bool hasChildDirectories)
        {
            return CreateTreeViewItem(new FBDValue(this, dInfo), hasChildDirectories);
        }

        private TreeViewItem CreateTreeViewItem(FBDValue value, bool hasChildDirectories)
        {
            TreeViewItem item = new TreeViewItem();
            item.Style = _treeViewItemStyle;
            item.Tag = value;
            item.Header = value.Name;
            item.Expanded += new RoutedEventHandler(item_Expanded);
            item.Collapsed += new RoutedEventHandler(item_Collapsed);

            // Set attached properties
            FBDItemProperties.SetIsCanceled(item, false);
            FBDItemProperties.SetIsLoaded(item, false);
            FBDItemProperties.SetIsLoading(item, false);

            if (value.Type == FBDValueType.Drive)
                FBDItemProperties.SetIconUri(item, "pack://application:,,,/_images/FolderBrowser/Drive.png");
            else if (value.Type == FBDValueType.Folder)
                FBDItemProperties.SetIconUri(item, "pack://application:,,,/_images/FolderBrowser/Folder.png");
            else
                FBDItemProperties.SetIconUri(item, "pack://application:,,,/_images/FolderBrowser/File.png");

            // Set context menu
            if (value.Type == FBDValueType.Folder)
            {
                ContextMenu menu = new ContextMenu();
                item.ContextMenu = menu;

                MenuItem menuItem = new MenuItem();
                menuItem.Header = Globalization.Delete;
                menuItem.Click += new RoutedEventHandler(menuItemDelete_Click);
                menu.Items.Add(menuItem);

                menuItem = new MenuItem();
                menuItem.Header = Globalization.Rename;
                menuItem.Click += new RoutedEventHandler(menuItemRename_Click);
                menu.Items.Add(menuItem);
            }

            // Check if there are some childs => Create a dummy item
            if (hasChildDirectories)
                item.Items.Add(new TreeViewItem());

            return item;
        }

        #endregion

        #region TreeViewItem : Expand

        void item_Collapsed(object sender, RoutedEventArgs e)
        {
            TreeViewItem item = e.OriginalSource as TreeViewItem;

            FBDValue value = item.Tag as FBDValue;
            if (value.Type == FBDValueType.Folder)
                FBDItemProperties.SetIconUri(item, "pack://application:,,,/_images/FolderBrowser/Folder.png");
        }

        void item_Expanded(object sender, RoutedEventArgs e)
        {
            // Load the sub items.
            TreeViewItem item = e.OriginalSource as TreeViewItem;

            FBDValue value = item.Tag as FBDValue;
            if (value.Type == FBDValueType.Folder)
                FBDItemProperties.SetIconUri(item, "pack://application:,,,/_images/FolderBrowser/Folder_open.png");

            // Check ?
            if (FBDItemProperties.GetIsLoaded(item))
                return;

            // Set attached properties
            FBDItemProperties.SetIsLoaded(item, true);
            FBDItemProperties.SetIsLoading(item, true);

            // Clear
            item.Items.Clear();

            // Load
            ThreadPool.QueueUserWorkItem(new WaitCallback(AsyncLoadTreeViewItem), item);
        }

        private void AsyncLoadTreeViewItem(object state)
        {
            TreeViewItem item = state as TreeViewItem;

            FBDValue value = null;
            Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
                                (DelegateInvoker)delegate
                                {
                                    value = item.Tag as FBDValue;
                                });

            string path;
            if (value.Type == FBDValueType.Drive)
                path = value.DriveInfo.RootDirectory.FullName;
            else
                path = value.DirectoryInfo.FullName;

            try
            {
                string[] directories = Directory.GetDirectories(path);
                for (int index = 0; index < directories.Length; index++)
                {
                    if (_isDialogClosed)
                        return;

                    DirectoryInfo info = new DirectoryInfo(directories[index]);

                    // Is it a hidden folder or not
                    if ((info.Attributes & FileAttributes.System) == FileAttributes.System)
                        continue;
                    else if ((info.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
                        continue;

                    // Check if there are some childs => Create a dummy item
                    bool hasChildDirectories = true;
                    try
                    {
                        if (info.GetDirectories().Length < 1)
                            hasChildDirectories = false;
                    }
                    catch
                    {
                        hasChildDirectories = false;
                    }

                    Dispatcher.BeginInvoke(
                            System.Windows.Threading.DispatcherPriority.Normal,
                            (DelegateInvoker)delegate
                            {
                                item.Items.Add(CreateTreeViewItem(info, hasChildDirectories));
                            });

                    if (index < directories.Length - 1)
                        Thread.Sleep(50);
                }
            }
            catch
            {
            }
            finally
            {
                Dispatcher.BeginInvoke(
                    System.Windows.Threading.DispatcherPriority.Normal,
                    (DelegateInvoker)delegate
                    {
                        FBDItemProperties.SetIsLoading(item, false);
                    });
            }
        }

        #endregion

        #region TreeView : SelectedItemChanged

        void treeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            if (treeView.SelectedItem != null)
            {
                FBDValue value = ((TreeViewItem)treeView.SelectedItem).Tag as FBDValue;

                if (value.Type == FBDValueType.Folder)
                    btnDeleteFolder.Visibility = Visibility.Visible;
                else
                    btnDeleteFolder.Visibility = Visibility.Collapsed;

                btnMakeFolder.Visibility = Visibility.Visible;
            }
            else
            {
                btnDeleteFolder.Visibility = Visibility.Collapsed;
                btnMakeFolder.Visibility = Visibility.Collapsed;
            }
        }

        #endregion

        #region Context menu

        void menuItemDelete_Click(object sender, RoutedEventArgs e)
        {
            btnDeleteFolder_Click(null, null);
        }

        void menuItemRename_Click(object sender, RoutedEventArgs e)
        {
            FBDValue value = ((TreeViewItem)treeView.SelectedItem).Tag as FBDValue;

            QuestionDialog dialog = new QuestionDialog(QuestionDialog.QuestionDialogType.OkCancel, Globalization.FolderBrowserDialog_EnterFolderName, null, value.Name, null, 350);
            dialog.ShowDialog(Host.ApplicationDesktop,
                                                new ChildWindowDialogClosedHandler(OnRename_OnDialogClosed));
        }

        private void OnRename_OnDialogClosed(ChildWindowDecorator window)
        {
            QuestionDialog dialog = window.Parent as QuestionDialog;

            if (!dialog.IsOkYes)
                return;

            //---- Rename the folder
            FBDValue value = ((TreeViewItem)treeView.SelectedItem).Tag as FBDValue;

            // Old
            string oldDirectory = value.DirectoryInfo.FullName;

            // New
            string newDirectory = value.DirectoryInfo.Parent.FullName + "\\" + dialog.Value;

            try
            {
                Directory.Move(oldDirectory, newDirectory);
                value.DirectoryInfo = new DirectoryInfo(newDirectory);
            }
            catch
            {
                return;
            }

            //---- Create in the tree view
            ((TreeViewItem)treeView.SelectedItem).Header = value.Name;
        }

        #endregion

        #region Add / Delete folder

        void btnDeleteFolder_Click(object sender, RoutedEventArgs e)
        {
            QuestionDialog dialog = new QuestionDialog(QuestionDialog.QuestionDialogType.OkCancel, Globalization.AreYouSure, null, null, null, 200);
            dialog.ShowDialog(Host.ApplicationDesktop,
                                                new ChildWindowDialogClosedHandler(OnDelete_OnDialogClosed));
        }

        private void OnDelete_OnDialogClosed(ChildWindowDecorator window)
        {
            QuestionDialog dialog = window.Parent as QuestionDialog;

            if (!dialog.IsOkYes)
                return;

            //---- Delete the folder
            FBDValue value = ((TreeViewItem)treeView.SelectedItem).Tag as FBDValue;
            Directory.Delete(value.DirectoryInfo.FullName);

            //---- Remove in the tree view
            TreeViewItem parent = (treeView.SelectedItem as TreeViewItem).Parent as TreeViewItem;
            parent.Items.Remove(treeView.SelectedItem);
        }

        void btnMakeFolder_Click(object sender, RoutedEventArgs e)
        {
            QuestionDialog dialog = new QuestionDialog(QuestionDialog.QuestionDialogType.OkCancel, Globalization.FolderBrowserDialog_EnterFolderName, null, "", null, 350);
            dialog.ShowDialog(Host.ApplicationDesktop,
                                                new ChildWindowDialogClosedHandler(OnMake_OnDialogClosed));
        }

        private void OnMake_OnDialogClosed(ChildWindowDecorator window)
        {
            QuestionDialog dialog = window.Parent as QuestionDialog;

            if (!dialog.IsOkYes)
                return;

            //---- Create the folder
            FBDValue value = ((TreeViewItem)treeView.SelectedItem).Tag as FBDValue;
            string newDirectory = "";
            if (value.Type == FBDValueType.Drive)
                newDirectory = value.DriveInfo.Name + dialog.Value;
            else if (value.Type == FBDValueType.Folder)
                newDirectory = value.DirectoryInfo.FullName + "\\" + dialog.Value;

            DirectoryInfo info = null;
            try
            {
                info = Directory.CreateDirectory(newDirectory);
            }
            catch
            {
                return;
            }

            //---- Create in the tree view
            ((TreeViewItem)treeView.SelectedItem).Items.Add(CreateTreeViewItem(info, false));
        }

        #endregion

        #region Properties

        public bool IsOk
        {
            get
            {
                return _isOk;
            }
        }

        public string SelectedPath
        {
            get
            {
                return _selectedPath;
            }
        }

        #endregion

        #region ShowDialog

        /// <summary>
        /// Show the dialog.
        /// </summary>
        /// <param name="container">The desktop</param>
        /// <param name="handler">This handler handle when the dialog is closed</param>
        public void ShowDialog(Desktop container, ChildWindowDialogClosedHandler handler)
        {
            childWindowDecorator.ShowDialog(container, handler);
        }

        #endregion

    }

    internal enum FBDValueType
    {
        Drive,
        Folder,
        File
    }

    sealed internal class FBDValue
    {

        #region Variables

        internal DriveInfo DriveInfo;
        internal DirectoryInfo DirectoryInfo;
        internal FBDValueType Type;

        #endregion

        #region Constructor

        internal FBDValue(FolderBrowserDialog dialog, DriveInfo info)
        {
            DriveInfo = info;
            Type = FBDValueType.Drive;
        }

        internal FBDValue(FolderBrowserDialog dialog, DirectoryInfo info)
        {
            DirectoryInfo = info;
            Type = FBDValueType.Folder;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Returns the readable name;
        /// </summary>
        public string Name
        {
            get
            {
                if (Type == FBDValueType.Drive)
                    return DriveInfo.Name;
                return System.IO.Path.GetFileName(DirectoryInfo.FullName);
            }
        }

        #endregion

    }

}